package com.class06.advice;

import com.class06.point.TestPointCut;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Configuration;

/**
 * 切面类 = 切点 + 通知
 * 通知可以分为前置通知(@Before)
 * 可以利用JoinPoint获取连接点信息，如：方法名、参数、返回值等、
 * 返回通知(@AfterReturning) 可以获取返回值结果,通过returning属性指定返回值变量名
 * 、异常通知(@AfterThreading) 可以获取异常信息,通过throwing属性指定异常变量名
 * 后置通知(@After)、环绕通知(@Around)
 * 1 、研究输出顺序 ：无异常时是先前置通知、返回通知、后置通知
 */

//@Aspect  //相当于 <aop:aspectj-autoproxy/>  标识为切面 = 切点 + 通知
@Configuration  //相当于xml文件
public class LoggingAdvice {

    //    @Before是前置通知
//    execution(* com.class06.service.Calculator.add(..))  切点
//    execution 固定单词
//    语法格式：execution(权限修饰符 返回值 包名.类名.方法名(参数列表))
//    * com.class06.service.Calculator.add(..)
//    *表示任意  ..表示任意参数
//    注意点：多级包之间可以用..来表示，但是不能用*来表示
//  前置通知
    @Before("com.class06.point.TestPointCut.class06PointCut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("==============前置通知=================");
//        getArgs()获取方法的实际参数
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.print(arg + " ");
        }
//        getSignature() 获取方法签名信息
        Signature signature = joinPoint.getSignature();
        System.out.println("方法名：" + signature.getName());
        System.out.println("方法所属的类：" + signature.getDeclaringTypeName());
        System.out.println("方法所属的类：" + signature.getDeclaringType());
        System.out.println("方法所属的类的全路径：" + signature.toLongString());
        System.out.println("方法所属的类的简短路径：" + signature.toShortString());
        System.out.println("方法所属的类的描述：" + signature.getModifiers());

        System.out.println("计算开始");
    }


    // returning 属性值必须与形参的名称一致
    @AfterReturning(value = "com.class06.point.TestPointCut.class06PointCut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, int result) {

        System.out.println("==============返回通知=================");
        Object[] args = joinPoint.getArgs();
//        for (Object arg : args) {  iter
        for (Object arg : args) {
            System.out.println(arg);
        }

        System.out.println("计算结果是：" + result);
    }

    @AfterThrowing(value = "com.class06.point.TestPointCut.class06PointCut()", throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.out.println("==============异常通知=================");
        System.out.println("异常信息：" + ex);
        System.out.println("异常方法：" + joinPoint.getSignature().getName());
        System.out.println("方法执行时入参：" + joinPoint.getArgs());
        System.out.println("异常的原因：" + ex.getStackTrace()[0].toString());
        System.out.println("异常信息：" + ex.getMessage());
    }


//    切点表达式被提取，想要引用语法格式: value ="全类名.方法名()"
    @After("com.class06.point.TestPointCut.class06PointCut()")
    public void after() {
        System.out.println("==============后置通知=================");
    }

    /**
     *
     * 切点表达式是语法格式：
     * execution(访问修饰符 返回值 包名.类名.方法名(参数列表))
     * - 第一位：execution( ) 固定开头
     * - 第二位：方法访问修饰符
     *
     * ```Java
     * public private 直接描述对应修饰符即可
     * ```
     * - 第三位：方法返回值
     *
     * ```Java
     * int String void 直接描述返回值类型
     *
     * ```
     *
     *     注意：
     *
     *     特殊情况 不考虑 访问修饰符和返回值
     *
     *       execution(* * ) 这是错误语法
     *
     *       execution(*) == 你只要考虑返回值 或者 不考虑访问修饰符 相当于全部不考虑了
     * - 第四位：指定包的地址
     *
     * ```Java
     *  固定的包: com.atguigu.api | service | dao
     *  单层的任意命名: com.atguigu.*  = com.atguigu.api  com.atguigu.dao  * = 任意一层的任意命名
     *  任意层任意命名: com.. = com.atguigu.api.erdaye com.a.a.a.a.a.a.a  ..任意层,任意命名 用在包上!
     *  注意: ..不能用作包开头   public int .. 错误语法  com..
     *  找到任何包下: *..
     * ```
     * - 第五位：指定类名称
     *
     * ```Java
     * 固定名称: UserService
     * 任意类名: *
     * 部分任意: com..service.impl.*Impl
     * 任意包任意类: *..*
     *
     * ```
     * - 第六位：指定方法名称
     *
     * ```Java
     * 语法和类名一致
     * 任意访问修饰符,任意类的任意方法: * *..*.*
     * ```
     * - 第七位：方法参数
     *
     * ```Java
     * 第七位: 方法的参数描述
     *        具体值: (String,int) != (int,String) 没有参数 ()
     *        模糊值: 任意参数 有 或者 没有 (..)  ..任意参数的意识
     *        部分具体和模糊:
     *          第一个参数是字符串的方法 (String..)
     *          最后一个参数是字符串 (..String)
     *          字符串开头,int结尾 (String..int)
     *          包含int类型(..int..)
     * ```
     */

//    示例
//    1.查询某包某类下，访问修饰符是公有，返回值是int的全部方法

//    需要注意的是任意多层包不能与..开头，但是可以与*..配合使用  当任意权限和任意返回值时可以简写为一个*
//    execution(public int *...*.*(..))
//2.查询某包下类中第一个参数是String的方法
//    execution(* *..*.*(String..))
//3.查询全部包下，无参数的方法！
//    execution(* *..*())
//4.查询com包下，以int参数类型结尾的方法
//    execution(* com..*.*(..int))
//5.查询指定包下，Service开头类的私有返回值int的无参数方法
//    execution(private int com.class06.service.Service*.*())



}
